<?php
/**
 * Created by PhpStorm.
 * User: whwyy
 * Date: 2018/3/30 0030
 * Time: 14:09
 */

namespace Database;



use Database\base\DbBase;

class Connection extends DbBase
{
    public $cds = '';

    public $database = '';

    public $password = '';

    public $username = '';

    public $charset = 'utf-8';

    public $tablePrefix = '';

    /** @var \PDO */
    private $_pdo = NULL;

    private $tranceNum = 0;

    public $slave = NULL;

    public $slaveConfig = [];
    public $masterConfig = [];

    /** @var \PDO */
    private $_salvePdo = NULL;

    private $masterConnectDsn = '';
    private $slaveConnectDsn = '';

    /**
     * @return \PDO
     * 获取连接
     * insert
     * update
     * delete
     * @throws
     */
    public function getConnect($sql = NULL)
    {
        try {
            $pdo = $this->getPdo($sql);
            $pdo->getAttribute(\PDO::ATTR_SERVER_INFO);
            return $pdo;
        } catch (\PDOException|\Exception $exception) {
            return $this->getPdo($sql, TRUE);
        }
    }

    /**
     * @param $isUpdate
     * @param bool $isClear
     * @return \PDO
     * @throws \Exception
     */
    private function getPdo($sql, $isClear = FALSE)
    {
        $isUpdate = FALSE;
        if ($sql !== NULL) {
            $lower = strtolower(mb_substr($sql, 0, 6));
            $isUpdate = in_array($lower, ['insert', 'update', 'delete']);
        }
        if ($isUpdate || !$this->slave) {
            return $this->getMasterConnect($isClear);
        } else {
            return $this->getSlaveConnect($isClear);
        }
    }

    /**
     * @return \PDO
     * 获取主链接
     */
    private function getMasterConnect($isClear = FALSE)
    {
        if ($isClear) {
            $this->_pdo = NULL;
        } else if ($this->_pdo instanceof \PDO) {
            return $this->_pdo;
        }

        $config = ['cds' => $this->cds, 'username' => $this->username, 'password' => $this->password];

        $config = $this->mergeConfig($config, $this->masterConfig);

        DbPool::$poolName = $config['cds'];

        $this->masterConnectDsn = $config['cds'];

        $this->_pdo = DbPool::pop('MASTER');

        if ($this->_pdo instanceof \PDO) {
            return $this->_pdo;
        }
        $this->_pdo = DbPool::con($config['cds'], $config['username'], $config['password']);
        return $this->_pdo;
    }

    /**
     * @param array $master
     * @param array $other
     * @return array
     * 多配置项合并
     */
    private function mergeConfig(array $master, array $other = [])
    {
        if (empty($other)) {
            return $master;
        }

        $slave = array_merge($master, $other[array_rand($other)]);
        return $slave;
    }

    /**
     * @throws \Exception
     * @return \PDO
     */
    private function getSlaveConnect($isClear = FALSE)
    {
        if ($isClear) {
            $this->_salvePdo = NULL;
        }
        if ($this->_salvePdo instanceof \PDO) {
            return $this->_salvePdo;
        }

        $slave = $this->mergeConfig($this->slave, $this->slaveConfig);

        DbPool::setPoolName($slave['cds']);
        $this->slaveConnectDsn = $slave['cds'];

        $this->_salvePdo = DbPool::pop('SLAVE');
        if ($this->_salvePdo instanceof \PDO) {
            return $this->_salvePdo;
        }
        $this->_salvePdo = DbPool::con($slave['cds'], $slave['username'], $slave['password']);
        return $this->_salvePdo;
    }

    /**
     * @return $this
     */
    public function beginTransaction()
    {
        ++$this->tranceNum;
        if ($this->tranceNum == 1) {
            $this->getConnect()->beginTransaction();
        }
        return $this;
    }

    /**
     * 事务回滚
     */
    public function rollback()
    {
        if ($this->tranceNum == 1) {
            $this->getConnect()->rollBack();
            $this->recycling();
        } else {
            --$this->tranceNum;
        }
    }

    /**
     * 事务提交
     */
    public function commit()
    {
        if ($this->tranceNum == 1) {
            $this->getConnect()->commit();
            $this->recycling();
        } else {
            --$this->tranceNum;
        }
    }

    /**
     * @param $sql
     * @param array $attributes
     * @return Command
     * @throws
     */
    public function createCommand($sql, $attributes = [])
    {
        \Yoc::$app->error->writer('Mysql: ' . $sql);

        Debug::trance('build command.');
        $command = new Command([
            'sql' => $sql,
            'params' => $attributes,
            'pdo' => $this->getConnect($sql),
        ]);
        return $command->bindValues();
    }

    /**
     * @return QueryBuilder
     */
    public function getQueryBuilder()
    {
        return new QueryBuilder();
    }

    /**
     * 回收连接
     */
    public function recycling()
    {
        if (!$this->_pdo instanceof \PDO) {
            return $this->slaveRecycling();
        }
        echo 'db recycling master' . PHP_EOL;
        if ($this->_pdo->inTransaction()) {
            return $this->slaveRecycling();
        }
        DbPool::setPoolName($this->masterConnectDsn);
        DbPool::push($this->_pdo, 'MASTER');
        $this->_pdo = NULL;

        return $this->slaveRecycling();
    }


    public function slaveRecycling()
    {
        if (!$this->_salvePdo instanceof \PDO) {
            return;
        }
        DbPool::setPoolName($this->slaveConnectDsn);
        DbPool::push($this->_salvePdo, 'SALVE');
        $this->_salvePdo = NULL;
    }
}